package com.shareyi.baseproject.kernel.common.spi.lock;

import com.shareyi.baseproject.kernel.common.exception.ExceptionMaker;
import com.shareyi.baseproject.kernel.common.utils.LogHelper;
import com.shareyi.baseproject.kernel.common.utils.MyStringUtils;
import org.apache.commons.lang3.StringUtils;

import javax.annotation.Resource;
import java.util.Collection;
import java.util.List;
import java.util.Optional;

/**
 * 默认的分布式锁执行器
 *
 * @author david
 * @date 2021-09-01
 */
public class DefaultDistributedLockExecutor implements DistributedLockExecutor {

    /**
     * 分布式锁
     */
    @Resource
    private DistributedLock distributedLock;

    /**
     * @param lockKey
     * @param timeoutMSec         超时时间，毫秒
     * @param lockExecuteTemplate
     * @param <T>
     * @return
     */
    @Override
    public <T> T execute(String lockKey, long timeoutMSec, LockExecuteTemplate<T> lockExecuteTemplate) {
        Optional<String> locked = Optional.empty();
        try {
            locked = distributedLock.tryLock(lockKey, timeoutMSec);
            if (!locked.isPresent()) {
                String msg = lockExecuteTemplate.getLockFailedMsg();
                msg = StringUtils.isEmpty(msg) ? "操作正在处理，请稍后提交!" : msg;
                ExceptionMaker.throwLockException(msg);
            }
            return lockExecuteTemplate.execute();
        } finally {
            if (locked.isPresent()) {
                try {
                    distributedLock.unlock(lockKey, locked.get());
                } catch (Exception e) {
                    //最好推送异步解锁任务，防止加锁过长
                    LogHelper.EXCEPTION.error("解锁失败,只能等待, lockKey={}", lockKey, e);
                }
            }
        }
    }

    @Override
    public <T> T executeBatch(List<String> lockKeyList, long timeoutMSec, LockExecuteTemplate<T> lockExecuteTemplate) {
        LockListResult lockListResult = null;
        try {
            lockListResult = this.lockList(lockKeyList, timeoutMSec);
            if (!lockListResult.isAllLocked()) {
                String msg = lockExecuteTemplate.getLockFailedMsg();
                msg = StringUtils.isEmpty(msg) ? "操作正在处理，请稍后提交!" : msg;
                ExceptionMaker.throwLockException(msg);
            }
            return lockExecuteTemplate.execute();
        } finally {
            if (lockListResult != null && lockListResult.hasLocked()) {
                for (String lockKey : lockListResult.getLockedList()) {
                    try {
                        distributedLock.unlock(lockKey, lockListResult.getUuid());
                    } catch (Exception e) {
                        //最好推送异步解锁任务，防止加锁过长
                        LogHelper.EXCEPTION.error("解锁失败,只能等待, lockKey={}", lockKey, e);
                    }

                }
            }
        }
    }

    /**
     * 执行批量锁定
     *
     * @param itemList
     * @param timeoutMSec
     * @return
     */
    private LockListResult lockList(Collection<String> itemList, long timeoutMSec) {
        String uuid = MyStringUtils.generateUUID();
        LockListResult lockListResult = LockListResult.create(uuid);
        for (String item : itemList) {
            Optional<String> locked = distributedLock.tryLock(item, uuid, timeoutMSec);
            if (locked.isPresent()) {
                lockListResult.appendLock(item);
            } else {
                lockListResult.appendLockFailure(item);
            }
        }
        return lockListResult;
    }
}
